﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TasksModel
{
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://mp.tasks.com/2010/")]
    public class TaskGroup : IdentifiedObject
    {
        #region Properties

        private TaskList _importantUrgentTasks = new TaskList();

        /// <summary>
        /// Do not set manually.
        /// </summary>
        public TaskList ImportantUrgentTasks
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return _importantUrgentTasks; }
            [System.Diagnostics.DebuggerStepThrough]
            set { _importantUrgentTasks = value; }
        }

        private TaskList _importantNotUrgentTasks = new TaskList();

        /// <summary>
        /// Do not set manually.
        /// </summary>
        public TaskList ImportantNotUrgentTasks
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return _importantNotUrgentTasks; }
            [System.Diagnostics.DebuggerStepThrough]
            set { _importantNotUrgentTasks = value; }
        }

        private TaskList _unimportantUrgentTasks = new TaskList();

        /// <summary>
        /// Do not set manually.
        /// </summary>
        public TaskList UnimportantUrgentTasks
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return _unimportantUrgentTasks; }
            [System.Diagnostics.DebuggerStepThrough]
            set { _unimportantUrgentTasks = value; }
        }

        private TaskList _unimportantNotUrgentTasks = new TaskList();

        public TaskList UnimportantNotUrgentTasks
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return _unimportantNotUrgentTasks; }
            [System.Diagnostics.DebuggerStepThrough]
            set { _unimportantNotUrgentTasks = value; }
        }

        public TaskList this[TaskClass type]
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                switch (type)
                {
                    case TaskClass.ImportantNotUrgent:
                        return _importantNotUrgentTasks;
                    case TaskClass.ImportantUrgent:
                        return _importantUrgentTasks;
                    case TaskClass.UnimportantNotUrgent:
                        return _unimportantNotUrgentTasks;
                    case TaskClass.UnimportantUrgent:
                        return _unimportantUrgentTasks;
                    default:
                        throw new ArgumentException("type");
                }
            }
        }

        public Task this[TaskClass type, int index]
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                switch (type)
                {
                    case TaskClass.ImportantNotUrgent:
                        return _importantNotUrgentTasks[index];
                    case TaskClass.ImportantUrgent:
                        return _importantUrgentTasks[index];
                    case TaskClass.UnimportantNotUrgent:
                        return _unimportantNotUrgentTasks[index];
                    case TaskClass.UnimportantUrgent:
                        return _unimportantUrgentTasks[index];
                    default:
                        throw new ArgumentException("type");
                }
            }
        }

        /// <summary>
        /// Total in all lists.
        /// </summary>
        public int Count
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return
                    _importantNotUrgentTasks.Count +
                    _importantUrgentTasks.Count +
                    _unimportantNotUrgentTasks.Count +
                    _unimportantUrgentTasks.Count;
            }
        }

        private TaskList _archived = new TaskList();

        /// <summary>
        /// All items moved to archive. Do not set manually.
        /// </summary>
        public TaskList Archived
        {
            [System.Diagnostics.DebuggerStepThrough]
            get { return _archived; }
            [System.Diagnostics.DebuggerStepThrough]
            set { _archived = value; }
        }

        /// <summary>
        /// All but archived. Not sorted. Not cached.
        /// </summary>
        [System.Xml.Serialization.XmlIgnore]
        public IEnumerable<Task> AllTasks
        {
            [System.Diagnostics.DebuggerStepThrough]
            get
            {
                return _importantUrgentTasks.
                    Concat(_importantNotUrgentTasks).
                    Concat(_unimportantUrgentTasks).
                    Concat(_unimportantNotUrgentTasks);
            }
        }
        
        #endregion

        #region Constructors

        public TaskGroup(Guid id)
        {
            ID = id;
        }

        public TaskGroup()
            : this(Guid.NewGuid())
        {
        }

        #endregion

        #region Methods

        public void MoveToArchive(Task task)
        {
            switch (task.Class)
            {
                case TaskClass.ImportantNotUrgent:
                    _importantNotUrgentTasks.RemoveOrderAware(task);
                    break;
                case TaskClass.ImportantUrgent:
                    _importantUrgentTasks.RemoveOrderAware(task);
                    break;
                case TaskClass.UnimportantNotUrgent:
                    _unimportantNotUrgentTasks.RemoveOrderAware(task);
                    break;
                case TaskClass.UnimportantUrgent:
                    _unimportantUrgentTasks.RemoveOrderAware(task);
                    break;
                default:
                    throw new ArgumentException("task");
            }
            System.Diagnostics.Debug.Assert(!_archived.Contains(task));
            task.Archived = true;
            _archived.Append(task);
        }

        public void AddTask(TaskClass type, Task task)
        {
            task.Class = type;
            switch (type)
            {
                case TaskClass.ImportantNotUrgent:
                    _importantNotUrgentTasks.Append(task);
                    break;
                case TaskClass.ImportantUrgent:
                    _importantUrgentTasks.Append(task);
                    break;
                case TaskClass.UnimportantNotUrgent:
                    _unimportantNotUrgentTasks.Append(task);
                    break;
                case TaskClass.UnimportantUrgent:
                    _unimportantUrgentTasks.Append(task);
                    break;
                default:
                    throw new ArgumentException("type");
            }
        }

        public void Remove(Task task)
        {
            switch (task.Class)
            {
                case TaskClass.ImportantNotUrgent:
                    _importantNotUrgentTasks.RemoveOrderAware(task);
                    break;
                case TaskClass.ImportantUrgent:
                    _importantUrgentTasks.RemoveOrderAware(task);
                    break;
                case TaskClass.UnimportantNotUrgent:
                    _unimportantNotUrgentTasks.RemoveOrderAware(task);
                    break;
                case TaskClass.UnimportantUrgent:
                    _unimportantUrgentTasks.RemoveOrderAware(task);
                    break;
                default:
                    throw new ArgumentException("task");
            }
        }

        public Task Find(Guid id, bool includeArchived)
        {
            Task res;
            res = _importantNotUrgentTasks.FirstOrDefault(t => t.ID.Equals(id));
            if (res == null)
                res = _importantUrgentTasks.FirstOrDefault(t => t.ID.Equals(id));
            if (res == null)
                res = _unimportantNotUrgentTasks.FirstOrDefault(t => t.ID.Equals(id));
            if (res == null)
                res = _unimportantUrgentTasks.FirstOrDefault(t => t.ID.Equals(id));
            if (res == null && includeArchived)
                res = _archived.FirstOrDefault(t => t.ID.Equals(id));
            return res;
        }

        public Task Find(string name, bool includeArchived)
        {
            Task res;
            res = _importantNotUrgentTasks.FirstOrDefault(t => t.Name.Equals(name));
            if (res == null)
                res = _importantUrgentTasks.FirstOrDefault(t => t.Name.Equals(name));
            if (res == null)
                res = _unimportantNotUrgentTasks.FirstOrDefault(t => t.Name.Equals(name));
            if (res == null)
                res = _unimportantUrgentTasks.FirstOrDefault(t => t.Name.Equals(name));
            if (res == null && includeArchived)
                res = _archived.FirstOrDefault(t => t.Name.Equals(name));
            return res;
        }

        public List<Task> SearchByName(string name, bool includeArchived)
        {
            Func<Task, bool> search = delegate(Task t)
                {
                    return t.Name != null &&
                        t.Name.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0;
                };
            List<Task> res = new List<Task>();
            res.AddRange(_importantNotUrgentTasks.Where(search));
            res.AddRange(_importantUrgentTasks.Where(search));
            res.AddRange(_unimportantNotUrgentTasks.Where(search));
            res.AddRange(_unimportantUrgentTasks.Where(search));
            if (includeArchived)
                res.AddRange(_archived.Where(search));
            return res;
        }

        public List<Task> SearchByExtReference(string externalReference, bool includeArchived)
        {
            Func<Task, bool> search = delegate(Task t)
            {
                return t.ExternalReference != null &&
                    t.ExternalReference.IndexOf(externalReference, StringComparison.Ordinal) >= 0;
            };
            List<Task> res = new List<Task>();
            res.AddRange(_importantNotUrgentTasks.Where(search));
            res.AddRange(_importantUrgentTasks.Where(search));
            res.AddRange(_unimportantNotUrgentTasks.Where(search));
            res.AddRange(_unimportantUrgentTasks.Where(search));
            if (includeArchived)
                res.AddRange(_archived.Where(search));
            return res;
        }

        public List<Task> SearchByContent(string name, bool includeArchived)
        {
            Func<Task, bool> search = delegate(Task t)
            {
                return t.RichContent != null &&
                    t.RichContent.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0;
            };
            List<Task> res = new List<Task>();
            res.AddRange(_importantNotUrgentTasks.Where(search));
            res.AddRange(_importantUrgentTasks.Where(search));
            res.AddRange(_unimportantNotUrgentTasks.Where(search));
            res.AddRange(_unimportantUrgentTasks.Where(search));
            if (includeArchived)
                res.AddRange(_archived.Where(search));
            return res;
        }

        public void Clear()
        {
            _importantUrgentTasks.Clear();
            _importantNotUrgentTasks.Clear();
            _unimportantUrgentTasks.Clear();
            _unimportantNotUrgentTasks.Clear();
            _archived.Clear();
        }

        public void MergeWith(TaskGroup tg)
        {
            _importantUrgentTasks.MergeWith(tg.ImportantUrgentTasks);
            _importantNotUrgentTasks.MergeWith(tg.ImportantNotUrgentTasks);
            _unimportantUrgentTasks.MergeWith(tg.UnimportantUrgentTasks);
            _unimportantNotUrgentTasks.MergeWith(tg.UnimportantNotUrgentTasks);
            _archived.MergeWith(tg.Archived);
        }

        public bool CheckAuthorUsed(TaskAuthor ta)
        {
            if (_importantUrgentTasks.CheckAuthorUsed(ta))
                return true;
            if (_importantNotUrgentTasks.CheckAuthorUsed(ta))
                return true;
            if (_unimportantUrgentTasks.CheckAuthorUsed(ta))
                return true;
            if (_unimportantNotUrgentTasks.CheckAuthorUsed(ta))
                return true;
            if (_archived.CheckAuthorUsed(ta))
                return true;
            return false;
        }

        public bool CheckStatusUsed(TaskStatus ts)
        {
            if (_importantUrgentTasks.CheckStatusUsed(ts))
                return true;
            if (_importantNotUrgentTasks.CheckStatusUsed(ts))
                return true;
            if (_unimportantUrgentTasks.CheckStatusUsed(ts))
                return true;
            if (_unimportantNotUrgentTasks.CheckStatusUsed(ts))
                return true;
            if (_archived.CheckStatusUsed(ts))
                return true;
            return false;
        }

        public bool CheckFlagUsed(TaskFlag tf)
        {
            if (_importantUrgentTasks.CheckFlagUsed(tf))
                return true;
            if (_importantNotUrgentTasks.CheckFlagUsed(tf))
                return true;
            if (_unimportantUrgentTasks.CheckFlagUsed(tf))
                return true;
            if (_unimportantNotUrgentTasks.CheckFlagUsed(tf))
                return true;
            if (_archived.CheckFlagUsed(tf))
                return true;
            return false;
        }
        #endregion
    }
}